
package org.spiderflow.core.expression.parsing.token;

import org.spiderflow.core.expression.parsing.CharacterStream;

import java.util.Arrays;
import java.util.Objects;

/**
 * Enumeration of token types. A token type consists of a representation for error messages, and may optionally specify a literal
 * to be used by the {@link CharacterStream} to recognize the token. Token types are sorted by their literal length to easy
 * matching of token types with common prefixes, e.g. "<" and "<=". Token types with longer literals are matched first.
 *
 * @author zsh
 */
public enum TokenType {
    // @off
    TEXT_BLOCK("a text block"),
    PERIOD(".", "."),
    COMMA(",", ","),
    SEMICOLON(";", ";"),
    COLON(":", ":"),
    PLUS("+", "+"),
    MINUS("-", "-"),
    ASTERISK("*", "*"),
    FORWARD_SLASH("/", "/"),
    POST_SLASH("\\", "\\"),
    PERCENTAGE("%", "%"),
    LEFT_PARENTHESES("(", "("),
    RIGHT_PARENTHESES(")", ")"),
    LEFT_BRACKET("[", "["),
    RIGHT_BRACKET("]", "]"),
    LEFT_CURLY("{", "{"),
    /**
     * special treatment!
     */
    RIGHT_CURLY("}"),
    LESS("<", "<"),
    GREATER(">", ">"),
    LESS_EQUAL("<=", "<="),
    GREATER_EQUAL(">=", ">="),
    EQUAL("==", "=="),
    NOT_EQUAL("!=", "!="),
    ASSIGNMENT("=", "="),
    AND("&&", "&&"),
    OR("||", "||"),
    XOR("^", "^"),
    NOT("!", "!"),
    QUESTION_MARK("?", "?"),
    DOUBLE_QUOTE("\"", "\""),
    SINGLE_QUOTE("'", "'"),
    BOOLEAN_LITERAL("true or false"),
    DOUBLE_LITERAL("a double floating point number"),
    FLOAT_LITERAL("a floating point number"),
    LONG_LITERAL("a long integer number"),
    INTEGER_LITERAL("an integer number"),
    SHORT_LITERAL("a short integer number"),
    BYTE_LITERAL("a byte integer number"),
    CHARACTER_LITERAL("a character"),
    STRING_LITERAL("a string"),
    NULL_LITERAL("null"),
    IDENTIFIER("an identifier");
    // @on

    private static final TokenType[] VALUES;

    static {
        // Sort the token types by their literal length. The character stream uses this
        // this order to match tokens with the longest length first.
        VALUES = TokenType.values();
        Arrays.sort(VALUES, (TokenType o1, TokenType o2) -> {
            if (Objects.equals(o1, o2)) {
                return 0;
            }
            if (o1.literal == null) {
                return 1;
            }
            if (o2.literal == null) {
                return -1;
            }
            return o2.literal.length() - o1.literal.length();
        });
    }

    private final String literal;
    private final String error;

    TokenType(String error) {
        this.literal = null;
        this.error = error;
    }

    TokenType(String literal, String error) {
        this.literal = literal;
        this.error = error;
    }

    /**
     * The literal to match, may be null.
     **/
    public String getLiteral() {
        return literal;
    }

    /**
     * The error string to use when reporting this token type in an error message.
     **/
    public String getError() {
        return error;
    }

    /**
     * Returns an array of token types, sorted in descending order based on their literal length. This is used by the
     * {@link CharacterStream} to match token types with the longest literal first. E.g. "<=" will be matched before "<".
     **/
    public static TokenType[] getSortedValues() {
        return VALUES;
    }
}
